// Nimbus SRP: Secure Remote Password (SRP-6a) protocol implementation for Java
//
//		Copyright (c) Connect2id Ltd. and others, 2011 - 2019

package com.espressif.provisioning.srp6a;


import java.io.Serializable;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


/**
 * The crypto parameters for the SRP-6a protocol. These must be agreed between
 * client and server before authentication and consist of a large safe prime
 * 'N', a corresponding generator 'g' and a hash function algorithm 'H'.
 *
 * <p>The practical approach is to have the server manage these and make them
 * available to clients on request. This way, the client does not need to
 * anticipate or otherwise keep track of which parameters are used for which
 * users or servers; it only needs to verify their validity, which can be done
 * mathematically or by simple table lookup.
 *
 * <p>For convenience this class includes a set of precomputed parameters.
 *
 * @author Vladimir Dzhuvinov
 * @author Bernard Wittwer
 */
public class SRP6CryptoParams implements Serializable {


	/**
	 * Serializable class version number
	 */
	private static final long serialVersionUID = -8758433435502894107L;

	// Pre-computed primes 'N' for a set of bitsizes

	/**
	 * Precomputed safe 256-bit prime 'N', as decimal. Origin SRP-6a demo
	 * at http://srp.stanford.edu/demo/demo.html.
	 */
	public static final BigInteger N_256 = new BigInteger("125617018995153554710546479714086468244499594888726646874671447258204721048803");


	/**
	 * Precomputed safe 512-bit prime 'N', as decimal. Origin SRP-6a demo
	 * at http://srp.stanford.edu/demo/demo.html.
	 */
	public static final BigInteger N_512 = new BigInteger("11144252439149533417835749556168991736939157778924947037200268358613863350040339017097790259154750906072491181606044774215413467851989724116331597513345603");


	/**
	 * Precomputed safe 768-bit prime 'N', as decimal. Origin SRP-6a demo
	 * at http://srp.stanford.edu/demo/demo.html.
	 */
	public static final BigInteger N_768 = new BigInteger("1087179135105457859072065649059069760280540086975817629066444682366896187793570736574549981488868217843627094867924800342887096064844227836735667168319981288765377499806385489913341488724152562880918438701129530606139552645689583147");


	/**
	 * Precomputed safe 1024-bit prime 'N', as decimal. Origin RFC 5054,
	 * appendix A.
	 */
	public static final BigInteger N_1024 = new BigInteger("167609434410335061345139523764350090260135525329813904557420930309800865859473551531551523800013916573891864789934747039010546328480848979516637673776605610374669426214776197828492691384519453218253702788022233205683635831626913357154941914129985489522629902540768368409482248290641036967659389658897350067939");


	/**
	 * Precomputed safe 1536-bit prime 'N', as decimal. Origin RFC 5054,
	 * appendix A.
	 */
	public static final BigInteger N_1536 = new BigInteger("1486998185923128292816507353619409521152457662596380074614818966810244974827752411420380336514078832314731499938313197533147998565301020797040787428051479639316928015998415709101293902971072960487527411068082311763171549170528008620813391411445907584912865222076100726050255271567749213905330659264908657221124284665444825474741087704974475795505492821585749417639344967192301749033325359286273431675492866492416941152646940908101472416714421046022696100064262587");


	/**
	 * Precomputed safe 2048-bit prime 'N', as decimal. Origin RFC 5054,
	 * appendix A.
	 */
	public static final BigInteger N_2048 = new BigInteger("21766174458617435773191008891802753781907668374255538511144643224689886235383840957210909013086056401571399717235807266581649606472148410291413364152197364477180887395655483738115072677402235101762521901569820740293149529620419333266262073471054548368736039519702486226506248861060256971802984953561121442680157668000761429988222457090413873973970171927093992114751765168063614761119615476233422096442783117971236371647333871414335895773474667308967050807005509320424799678417036867928316761272274230314067548291133582479583061439577559347101961771406173684378522703483495337037655006751328447510550299250924469288819");



	/**
	 * Precomputed safe 3072-bit prime 'N', as decimal. Origin RFC 5054,
	 * appendix A.
	 */
	public static final BigInteger N_3072 = BigIntegerUtils.fromHex("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF");


	/**
	 * Precomputed safe 4096-bit prime 'N', as decimal. Origin RFC 5054,
	 * appendix A.
	 */
	public static final BigInteger N_4096 = BigIntegerUtils.fromHex("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF");


	/**
	 * Precomputed safe 6144-bit prime 'N', as decimal. Origin RFC 5054,
	 * appendix A.
	 */
	public static final BigInteger N_6144 = BigIntegerUtils.fromHex("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF");


	/**
	 * Precomputed safe 8192-bit prime 'N', as decimal. Origin RFC 5054,
	 * appendix A.
	 */
	public static final BigInteger N_8192 = BigIntegerUtils.fromHex("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF");
	/**
	 * Generator 'g' parameter for {@link #N_256}, {@link #N_512},
	 * {@link #N_768}, {@link #N_1024}, {@link #N_1536}, and
	 * {@link #N_2048} as decimal.
	 */
	public static final BigInteger g_common = BigInteger.valueOf(2);

	/**
	 * Generator 'g' parameter for {@link #N_3072}, {@link #N_4096},
	 * and {@link #N_6144} as decimal.
	 */
	public static final BigInteger g_large = BigInteger.valueOf(5);

	/**
	 * Generator 'g' parameter for {@link #N_8192} as decimal.
	 */
	public static final BigInteger g_x_large = BigInteger.valueOf(19);

	/**
	 * The safe prime 'N'.
	 */
	public final BigInteger N;


	/**
	 * The corresponding generator 'g'.
	 */
	public final BigInteger g;


	/**
	 * The hash algorithm 'H'.
	 */
	public final String H;


	/**
	 * Returns an SRP-6a crypto parameters instance with precomputed 'N'
	 * and 'g' values and the specified hash algorithm 'H'.
	 *
	 * @param bitsize The preferred prime number bitsize. Must exist as a
	 *                precomputed constant.
	 * @param H       The preferred hash algorithm. Must be supported by the
	 *                default security provider of the underlying Java
	 *                runtime.
	 *
	 * @return The matching SRP-6a crypto parameters instance, or
	 *         {@code null} if no matching constants or hash algorithm
	 *         provider could be found.
	 */
	public static SRP6CryptoParams getInstance(final int bitsize, final String H) {

		if (H == null || H.isEmpty())
			throw new IllegalArgumentException("Undefined hash algorithm 'H'");

		switch (bitsize) {
			case 256:
				return new SRP6CryptoParams(N_256, g_common, H);
			case 512:
				return new SRP6CryptoParams(N_512, g_common, H);
			case 768:
				return new SRP6CryptoParams(N_768, g_common, H);
			case 1024:
				return new SRP6CryptoParams(N_1024, g_common, H);
			case 1536:
				return new SRP6CryptoParams(N_1536, g_common, H);
			case 2048:
				return new SRP6CryptoParams(N_2048, g_common, H);
			case 3072:
				return new SRP6CryptoParams(N_3072, g_large, H);
			case 4096:
				return new SRP6CryptoParams(N_4096, g_large, H);
			case 6144:
				return new SRP6CryptoParams(N_6144, g_large, H);
			case 8192:
				return new SRP6CryptoParams(N_8192, g_x_large, H);
			default:
				return null;
		}
	}


	/**
	 * Returns an SRP-6a crypto parameters instance with precomputed
	 * 512-bit prime 'N', matching 'g' value and "SHA-1" hash algorithm.
	 *
	 * @return SRP-6a crypto parameters instance with 512-bit prime 'N',
	 *         matching 'g' value and "SHA-1" hash algorithm.
	 */
	public static SRP6CryptoParams getInstance() {

		return getInstance(512, "SHA-1");
	}


	/**
	 * Checks if the specified hash algorithm 'H' is supported by the
	 * default security provider of the underlying Java runtime.
	 *
	 * @param H The hash algorithm to check, e.g. "SHA-1".
	 *
	 * @return {@code true} if the hash algorightm is supported, else
	 *         {@code false}.
	 */
	public static boolean isSupportedHashAlgorithm(final String H) {

		try {
			MessageDigest.getInstance(H);

			return true; // success

		} catch (NoSuchAlgorithmException e) {

			return false; // not supported
		}
	}


	/**
	 * Creates a new SRP-6a crypto parameters instance. Note that the 'N'
	 * and 'g' values are not validated, nor is the 'H' support by the
	 * default security provider of the underlying Java runtime.
	 *
	 * @param N A large safe prime for the 'N' parameter. Must not be
	 *          {@code null}.
	 * @param g A corresponding generator for the 'g' parameter. Must not be
	 *          {@code null}.
	 * @param H A hash algorithm. Must by supported by the default security
	 *          provider of the underlying Java runtime. Must not be
	 *          {@code null}.
	 */
	public SRP6CryptoParams(final BigInteger N, final BigInteger g, final String H) {

		if (N == null)
			throw new IllegalArgumentException("The prime parameter 'N' must not be null");

		this.N = N;

		if (g == null)
			throw new IllegalArgumentException("The generator parameter 'g' must not be null");

		if (g.equals(BigInteger.ONE))
			throw new IllegalArgumentException("The generator parameter 'g' must not be 1");

		if (g.equals(N.subtract(BigInteger.ONE)))
			throw new IllegalArgumentException("The generator parameter 'g' must not equal N - 1");

		if (g.equals(BigInteger.ZERO))
			throw new IllegalArgumentException("The generator parameter 'g' must not be 0");


		this.g = g;


		if (H == null || H.isEmpty())
			throw new IllegalArgumentException("Undefined hash algorithm 'H'");

		if (! isSupportedHashAlgorithm(H))
			throw new IllegalArgumentException("Unsupported hash algorithm 'H': " + H);

		this.H = H;
	}


	/**
	 * Returns a new message digest instance for the hash algorithm 'H'.
	 *
	 * @return A new message digest instance or {@code null} if not
	 *         supported by the default security provider of the underlying
	 *         Java runtime.
	 */
	public MessageDigest getMessageDigestInstance() {

		try {
			return MessageDigest.getInstance(H);

		} catch (NoSuchAlgorithmException e) {

			return null;
		}
	}
}
